package com.edu.juc.jmm;

import java.util.concurrent.CountDownLatch;

/**
 * JMM: Java内存模型
 * 因为有这么多的缓存（CPU和物理主内存的速度不一致），
 * CPU的运行并不是直接操作内存而是先把内存里面的数据读到缓存，而内存的读写操作会造成不一致的问题。
 * JVM规范中，试图定义一种Java内存模型(Java Memory Model) 来屏蔽掉各种硬件和操作系统的内存访问差异，
 * 以实现让Java程序在各种平台下都能达到一致的内存访问效果。
 * ====> JMM
 * <p>
 * 原则：JMM的关键技术点都是围绕多线程的原子性、可见性和有序性展开的。
 * 1. 通过JMM来实现线程和主内存之间的抽象关系；
 * 2. 屏蔽各个硬件平台和操作系统的内存访问差异，以实现让Java程序在各种平台下都能达到一致的内存访问效果。
 * <p>
 * JMM三大特性：
 * 1. 可见性
 * --- 是指当一个线程修改了某一个共享变量的值，其他线程能否立即直到该变更，
 * --- JMM规定了所有的变量都存储在主内存中。
 * --- 每个线程都有自己的工作内存，线程自己的工作内存中保存了该线程使用到的变量的主内存副本拷贝，
 * ---   线程对变量的所有操作都必须在工作内存中进行，而不能直接读写主内存中的变量。
 * ---   不同线程之间也无法直接访问对方工作内存中的变量，线程间变量值的传递需要通过主内存来完成。
 * ===========> 缓存一致性协议 or 总线锁机制 ===> JMM控制
 * 2. 原子性
 * --- 指一个操作是不可打断的，即多线程环境下，操作不能被其他线程干扰。
 * 3. 有序性
 * --- 为提升性能，编译器和处理器通常会对指令序列进行重新排序。
 * --- Java规范规定JVM线程内部维持顺序化语义，
 * --- 即只要程序的最终结果与它顺序化执行的结果相同，那么指令的顺序执行可以与代码顺序不一致，此过程叫指令的重排序。
 * <p>
 * 先行发生原则：happens-before
 * 1.在JMM中，如果一个操作执行的结果需要另一个操作可见，或者 代码重排序，
 * 那么这两个操作之间必须存在happens-before（先行发生）原则。
 * 逻辑上的先后关系。
 * 2.happens-before总原则：
 * -- ① 如果一个操作happens-before另一个操作，那么第一个操作的执行结果将对第二个操作可见。
 * ---   而且第一个操作的执行顺序排在第二个操作之前。
 * -- ② 两个操作之间存在happens-before关系，并不意味着一定要按照happens-before原则制定的顺序来执行。
 * ---  如果重排序之后的执行结果与按照happens-before原则执行的结果相同，那么重排序并不非法。
 * 3.happens-before原则 8 条
 * ① 次序规则：一个线程内，按照代码顺序，写在前面的操作先行发生于写在后面的操作。
 * ② 锁定规则：一个unlock操作先行发生于后面（时间上的先后）对同一个锁的lock操作。
 * ③ volatile变量规则：一个volatile变量的写操作先行发生于后面对这个变量的读操作。前面的写对后面（时间）的读可见。
 * ④ 传递规则：如果操作a先行发生于操作b，操作b先行发生于操作c，那么操作a先行发生于操作c。
 * ⑤ 线程启动规则(Thread Start Rule)：线程t1启动（调用start）先行发生于线程t1中的第一个操作。
 * ⑥ 线程中断规则(Thread Interruption Rule)：对线程的interrupt()方法调用先行发生于对isInterrupted()的调用。
 * ⑦ 线程终止规则(Thread Termination Rule)：线程中的所有操作都先行发生于对此线程的终止检测。可通过isAlive()方法检测线程是否终止。
 * ⑧ 对象终结规则(Finalizer Rule)：一个对象的初始化完成（构造函数执行结束）先行发生于它的finalize()方法调用。
 */
public class MainJMM {
    static volatile int num = 0;

    public static void main(String[] args) {
        CountDownLatch count = new CountDownLatch(10000);
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    synchronized (MainJMM.class) {
                        num++;
                    }
                }
                count.countDown();
            }).start();
        }

        try {
            count.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(num);
    }
}
